﻿using System.Collections.ObjectModel;
using OpenQA.Selenium;

namespace Eswat.Services.WebDriver
{
	/// <summary>
	/// WebDriver test service.
	/// </summary>
	public class WebDriverTestService : ITestService, IWebDriver
	{
		/// <summary>
		/// The default name of this test service.
		/// </summary>
		public const string DefaultServiceName = "WebDriver";

		private IWebDriver _driver;

		/// <summary>
		/// Initializes a new instance of the <see cref="WebDriverTestService"/> class.
		/// </summary>
		public WebDriverTestService()
			: this(DefaultServiceName)
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="WebDriverTestService"/> class.
		/// </summary>
		/// <param name="name">The name.</param>
		public WebDriverTestService(string name)
		{
			this.Name = name;
		}

		/// <summary>
		/// Gets the unique service name.
		/// </summary>
		public string Name { get; private set; }

		/// <summary>
		/// Opens the specified web browser type.
		/// </summary>
		/// <param name="webBrowserType">Type of the web browser.</param>
		public void Open(WebBrowserType webBrowserType)
		{
			this._driver = webBrowserType.Create();
		}

		/// <summary>
		/// Close the current window, quitting the browser if it is the last window currently open.
		/// </summary>
		public void Close()
		{
			this._driver.Close();

			if (this._driver.WindowHandles.Count == 0)
			{
				this._driver.Dispose();
				this._driver = null;
			}
		}

		/// <summary>
		/// Gets the current window handle, which is an opaque handle to this
		/// window that uniquely identifies it within this driver instance.
		/// </summary>
		public string CurrentWindowHandle
		{
			get { return this._driver.CurrentWindowHandle; }
		}

		/// <summary>
		/// Instructs the driver to change its settings.
		/// </summary>
		/// <returns>
		/// An <see cref="T:OpenQA.Selenium.IOptions"/> object allowing the user to change
		/// the settings of the driver.
		/// </returns>
		public IOptions Manage()
		{
			return this._driver.Manage();
		}

		/// <summary>
		/// Instructs the driver to navigate the browser to another location.
		/// </summary>
		/// <returns>
		/// An <see cref="T:OpenQA.Selenium.INavigation"/> object allowing the user to access
		/// the browser's history and to navigate to a given URL.
		/// </returns>
		public INavigation Navigate()
		{
			return this._driver.Navigate();
		}

		/// <summary>
		/// Gets the source of the page last loaded by the browser.
		/// </summary>
		public string PageSource
		{
			get { return this._driver.PageSource; }
		}

		/// <summary>
		/// Quits this driver, closing every associated window.
		/// </summary>
		public void Quit()
		{
			this._driver.Quit();
			this._driver.Dispose();
			this._driver = null;
		}

		/// <summary>
		/// Instructs the driver to send future commands to a different frame or window.
		/// </summary>
		/// <returns>
		/// An <see cref="T:OpenQA.Selenium.ITargetLocator"/> object which can be used to select
		/// a frame or window.
		/// </returns>
		public ITargetLocator SwitchTo()
		{
			return this._driver.SwitchTo();
		}

		/// <summary>
		/// Gets the title of the current browser window.
		/// </summary>
		public string Title
		{
			get { return this._driver.Title; }
		}

		/// <summary>
		/// Gets or sets the URL the browser is currently displaying.
		/// </summary>
		/// <seealso cref="M:OpenQA.Selenium.INavigation.GoToUrl(System.String)"/>
		///   
		/// <seealso cref="M:OpenQA.Selenium.INavigation.GoToUrl(System.Uri)"/>
		public string Url
		{
			get
			{
				return this._driver.Url;
			}

			set
			{
				this._driver.Url = value;
			}
		}

		/// <summary>
		/// Gets the window handles of open browser windows.
		/// </summary>
		public ReadOnlyCollection<string> WindowHandles
		{
			get { return this._driver.WindowHandles; }
		}

		/// <summary>
		/// Finds the first <see cref="T:OpenQA.Selenium.IWebElement"/> using the given method.
		/// </summary>
		/// <param name="by">The locating mechanism to use.</param>
		/// <returns>
		/// The first matching <see cref="T:OpenQA.Selenium.IWebElement"/> on the current context.
		/// </returns>
		/// <exception cref="T:OpenQA.Selenium.NoSuchElementException">If no element matches the criteria.</exception>
		public IWebElement FindElement(By by)
		{
			return this._driver.FindElement(by);
		}

		/// <summary>
		/// Finds all <see cref="T:OpenQA.Selenium.IWebElement">IWebElements</see> within the current context
		/// using the given mechanism.
		/// </summary>
		/// <param name="by">The locating mechanism to use.</param>
		/// <returns>
		/// A <see cref="T:System.Collections.ObjectModel.ReadOnlyCollection`1"/> of all <see cref="T:OpenQA.Selenium.IWebElement">WebElements</see>
		/// matching the current criteria, or an empty list if nothing matches.
		/// </returns>
		public ReadOnlyCollection<IWebElement> FindElements(By by)
		{
			return this._driver.FindElements(by);
		}

		/// <summary>
		/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
		/// </summary>
		public void Dispose()
		{
			this._driver.Dispose();
		}
	}
}
